#include "Requester.h"
#include <exception>
#include <stdio.h>
#include <stdlib.h>
#include "Link.h"
#include "Bundle.h"
#include "Util.h"
#include "HBSD.h"
#include "GBOF.h"
#include "ConfigFile.h"

using namespace std;

string Requester::xmlBanner;
string Requester::BPA_END;
string Requester::LOOPBACKADR;
	
Requester::Requester()
{
	defaultDestPort = 0;
	idBase = Util::to_string(this) + string("-") + Util::to_string(time(NULL)/1000) + string("-");
	injectIdSeq = 0;
	xmlBanner.assign("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
	BPA_END.assign("\n</bpa>");
	LOOPBACKADR.assign("127.0.0.1");
	defaultDestPort = 0;
	sourcePort = 0;
	sem_init(&methodSync, 0, 1);
}

Requester::~Requester()
{
	sem_destroy(&methodSync);
}
void Requester::init(string group, int port)  
{
	try 
	{
		defaultDestPort = port;
		
		// Init multicast a
		socketR = socket(AF_INET, SOCK_DGRAM, 0);
		
		string adr = HBSD::routerConf->getstring(string("loopbackAddress"), LOOPBACKADR);

		memset((char *) &defaultDestAddr, 0, sizeof(defaultDestAddr));

		defaultDestAddr.sin_family = AF_INET;
		defaultDestAddr.sin_addr.s_addr = inet_addr(adr.c_str());
		defaultDestAddr.sin_port = htons(defaultDestPort);
		//sourcePort = socket.getLocalPort();
		
	} 

	catch (exception &e) 
	{
		throw e;
	}
}
	

string Requester::getMsgStart() 
{
	if (HBSD::localEID.empty()) 	
	{
		// Local EID not yet known: don't cache value yet.
		string tmpXML = xmlBanner + string("\n<bpa eid=\"null\">\n  ");
		return tmpXML;
	}

	if (xmlWithBpaStart.length() == 0) 
	{
		constructStarts();
	}

	return xmlWithBpaStart;
}


bool Requester::xmlEncapsulateAndSend(string data, struct sockaddr_in & addr, int port) 
{
	try 
	{
		string strMsg = getMsgStart() + data + BPA_END;

		bind(socketR, (sockaddr *)&defaultDestAddr, sizeof(addr));

		sendto(socketR ,strMsg.c_str(),strMsg.length(), 0,(sockaddr *)&addr, sizeof(addr)) ;

		return true;
	} 
	
	catch (exception &e) 
	{
			if (HBSD::log->enabled(Logging::ERROR)) 
			{
				HBSD::log->error(string("Failed to send request to dtnd: ") + string(e.what()));
			}
			return false;
	}
}

bool Requester::requestSendBundle(Bundle* bundle, string linkId, int action) 
{
	string req = string("<send_bundle_request local_id=\"") + Util::to_string(bundle->localId) + string("\" link_id=\"") + linkId + string("\" fwd_action=\"");
	
	if (action == FWD_ACTION_FORWARD) 
	{
		req += string("forward\">");
	} else 
	{
		req += string("copy\">");
	}
	
	req += GBOF::xmlFromBundle(bundle) + string("</send_bundle_request>");
	
	return sendAsXML(req);
}

string Requester::requestInjectBundle(string source, string dest,string linkId, string payloadFile) 
{
	// Generate an id that will uniquely identify the injected bundle.
	string requestId =  idBase + Util::to_string(getAndIncrement());
	
	// Define the bundle to be forwarded with a hardcoded expiration.
	string req = string("<inject_bundle_request request_id=\"") + requestId + string("\" link_id=\"") + linkId + string("\" fwd_action=\"forward\" expiration=\"300\" payload_file=\"") + payloadFile + string("\">"); 
	req += string("<source uri=\"") + source + string("\"/>");
	req += string("<dest uri=\"") + dest + string("\"/>");
	req += string("</inject_bundle_request>");
	if (!sendAsXML(req)) 
	{
		return NULL;
	}

	return requestId;
}
	
bool Requester::requestDeleteBundle(Bundle* bundle) 
{
	string req = string("<delete_bundle_request local_id=\"") + Util::to_string(bundle->localId) + string("\">") + GBOF::xmlFromBundle(bundle) + string("</delete_bundle_request>");
	return sendAsXML(req);
}
	
bool Requester::requestCancelBundle(Bundle* bundle, Link* link) 
{
		string req = string("<cancel_bundle_request local_id=\"") + Util::to_string(bundle->localId) + string("\" link_id=\"") + link->id + string("\">") +  GBOF::xmlFromBundle(bundle) + string("</cancel_bundle_request>");
		return sendAsXML(req);
}

void Requester::constructStarts()
{
	sem_wait(&methodSync);

	if (bpaStart.empty()) 
	{
		bpaStart = string("<bpa eid=\"") + HBSD::localEID + string("\">");
	}

	if (xmlWithBpaStart.empty()) 
	{
		if (HBSD::log->enabled(Logging::TRACE)) 
		{
			// Add some whitespace if logging simply to make it easier
			// to read.
			xmlWithBpaStart = xmlBanner + string("\n") + bpaStart + string("\n  ");
		} else 
		{
			xmlWithBpaStart = xmlBanner + bpaStart;
		}
	}

	sem_post(&methodSync);
}
